//------------------------------------------------------------------------
// Abstract: Implement of class SQLiteDB
//
// Authoring: XiaoHanxiong
//
// Time: 2014.03.14
//
// Static test:
//
// Version history:
//		Ver_1.0	: 2014.03.14	XiaoHanxiong
//------------------------------------------------------------------------
#include "SQLiteDB.h"
#include "SQLite\sqlite3.h"


namespace Database
{
	const unsigned int TextBufferSize = 1024;


	SQLiteDB::SQLiteDB()
		: IDatabase(SQLite)
		, m_sqlite3_db(NULL)
		, m_sqlite_stmt(NULL)
		, m_result_code(SQLite_CodeUnValid)
		, m_callback(NULL)
	{
	}

	SQLiteDB::~SQLiteDB()
	{
		Close();
	}

	bool SQLiteDB::Open( const char* db , int flag)
	{
		int code = 0;
		const char* db_file = db;

		if(m_sqlite3_db != NULL)
			Close();

		code = sqlite3_open_v2(db_file, &m_sqlite3_db, flag, NULL);

		if(code != SQLITE_OK)
		{
			m_sqlite3_db = NULL;
			return false;
		}

		return true;
	}

	void SQLiteDB::Close()
	{
		sqlite3_finalize(m_sqlite_stmt);
		sqlite3_close(m_sqlite3_db);

		m_sqlite_stmt = NULL;
		m_sqlite3_db = NULL;
		m_result_code = SQLite_CodeUnValid;
	}

	bool SQLiteDB::ExecuteSQL( const char* sql )
	{
		if(m_sqlite3_db != NULL)
		{
			int code = sqlite3_exec(m_sqlite3_db, sql, NULL, 0, NULL);

			return code == SQLITE_OK ? true : false;
		}
		else return false;
	}

	bool SQLiteDB::PrepareSQL( const char* sql )
	{
		sqlite3_finalize(m_sqlite_stmt);
		m_sqlite_stmt = NULL;

		if(m_sqlite3_db != NULL)
		{
			int code = sqlite3_prepare_v2(m_sqlite3_db, sql, -1, 
				&m_sqlite_stmt, NULL);
			return code == SQLITE_OK ? true : false;
		}
		else return false;
	}

	void SQLiteDB::CloseSQL()
	{
		if(m_sqlite_stmt != NULL)
		{
			sqlite3_finalize(m_sqlite_stmt);
			m_sqlite_stmt = NULL;
		}
	}

	bool SQLiteDB::Step()
	{
		m_result_code = sqlite3_step(m_sqlite_stmt);
		if(m_result_code == SQLITE_ROW)
			return true;

		return false;
	}

	const char* SQLiteDB::GetLastError()
	{
		return sqlite3_errmsg(m_sqlite3_db);
	}

	ExeCallBack SQLiteDB::SetCallBackFunc( ExeCallBack callback )
	{
		ExeCallBack current = m_callback;
		m_callback = callback;
		return current;
	}

	bool SQLiteDB::Transaction( bool flag )
	{
		int code = SQLite_CodeUnValid;
		if(m_sqlite3_db != NULL)
		{
			if(flag == true) code = sqlite3_exec(m_sqlite3_db, "begin transaction;", NULL, 0, NULL);
			else code = sqlite3_exec(m_sqlite3_db, "end transaction;", NULL, 0, NULL);
		}

		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Commit()
	{
		return sqlite3_exec(m_sqlite3_db, "commit;", NULL, 0, NULL) == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Rollback()
	{
		return sqlite3_exec(m_sqlite3_db, "rollback;", NULL, 0, NULL) == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Insert()
	{
		if(sqlite3_step(m_sqlite_stmt) == SQLITE_DONE) 
		{
			int code_clear = sqlite3_clear_bindings(m_sqlite_stmt);
			int code_reset = sqlite3_reset(m_sqlite_stmt);

			return (code_clear == SQLITE_OK && code_reset == SQLITE_OK);
		}
		return false;
	}

	int SQLiteDB::CallBack( void* para, int column_count, char** p_value, char** p_name )
	{
		return 0;
	}


	/*
		@Abstract: 
			Set field value of an record by specify index or field name.
			The index of SQLite API (sqlite3_bind_*) begin from 1 to n,
			To keep the same with Set and Get, i make the index plus 1 for begining from 0 to n.
		@Overload List:
			unsigned char, unsigned int, unsigned __int64
			double, string, void*, null
	*/
	bool SQLiteDB::Set( int index, unsigned int value )
	{
		int code = sqlite3_bind_int(m_sqlite_stmt, index + 1, value);
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index, unsigned __int64 value )
	{
		int code = sqlite3_bind_int64(m_sqlite_stmt, index + 1, value);
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index, double value )
	{
		int code = sqlite3_bind_double(m_sqlite_stmt, index + 1, value);
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index, string& value )
	{
		int code = sqlite3_bind_text(m_sqlite_stmt, index + 1, value.c_str(), value.size(), NULL);;
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index, const char* text, int size )
	{
		int code = sqlite3_bind_text(m_sqlite_stmt, index + 1, text, size, NULL);;
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index, const void* value, int size )
	{
		int code = sqlite3_bind_blob(m_sqlite_stmt, index + 1, value, size, NULL);
		return code == SQLITE_OK ? true : false;
	}

	bool SQLiteDB::Set( int index )
	{
		int code = sqlite3_bind_null(m_sqlite_stmt, index + 1);
		return code == SQLITE_OK ? true : false;
	}

#if 0 // Set field value by field name
	bool SQLiteDB::Set( char* field, unsigned int value )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index, value);
	}

	bool SQLiteDB::Set( char* field, unsigned __int64 value )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index, value);
	}

	bool SQLiteDB::Set( char* field, double value )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index, value);
	}

	bool SQLiteDB::Set( char* field, string& value )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index, value);
	}

	bool SQLiteDB::Set( char* field, void* value, size_t size )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index, value, size);
	}

	bool SQLiteDB::Set( char* field )
	{
		int index = sqlite3_bind_parameter_index(m_sqlite_stmt, field);
		return Set(index);
	}
#endif

	/*
		@Abstract: 
			Get field value of an record by specify index.
			The index of SQLite API (sqlite3_column_*) begin from 1 to n,
		@Overload List:
			unsigned char, unsigned int, unsigned __int64
			double, string, void*, null
	*/
	bool SQLiteDB::Get( int index, unsigned int& value )
	{
		if(m_result_code == SQLITE_ROW && m_sqlite_stmt != NULL)
		{
			value = (unsigned int)sqlite3_column_int(m_sqlite_stmt, index);
			return true;
		}
		return false;
	}

	bool SQLiteDB::Get( int index, unsigned __int64& value )
	{
		if(m_result_code == SQLITE_ROW && m_sqlite_stmt != NULL)
		{
			value = (unsigned __int64)sqlite3_column_int64(m_sqlite_stmt, index);
			return true;
		}
		return false;
	}

	bool SQLiteDB::Get( int index, double& value )
	{
		if(m_result_code == SQLITE_ROW && m_sqlite_stmt != NULL)
		{
			value = (double)sqlite3_column_double(m_sqlite_stmt, index);
			return true;
		}
		return false;
	}

	bool SQLiteDB::Get( int index, string& value )
	{
		if(m_result_code == SQLITE_ROW && m_sqlite_stmt != NULL)
		{
			const char* data = (const char*)sqlite3_column_text(m_sqlite_stmt, index);
			int text_size = sqlite3_column_bytes(m_sqlite_stmt, index);
			
			value.clear();
			if(data != NULL && text_size != 0)
			{
				char text_buffer[TextBufferSize] = {0};
				// if text_size > TextBufferSize(1024), then cut off the string
				text_size = text_size < TextBufferSize ? text_size : TextBufferSize;
				memcpy(text_buffer, data, text_size);
				value = data;
			}
			return true;
		}
		return false;
	}

	bool SQLiteDB::Get( int index, const void*& value, int& size )
	{
		if(m_result_code == SQLITE_ROW && m_sqlite_stmt != NULL)
		{
			value = sqlite3_column_blob(m_sqlite_stmt, index);
			size = sqlite3_column_bytes(m_sqlite_stmt, index);
			return true;
		}
		return false;
	}

	bool SQLiteDB::GetColumnCount(int& count)
	{
		if(m_sqlite_stmt != NULL)
		{
			count = sqlite3_column_count(m_sqlite_stmt);
			return true;
		}
		return false;
	}

	bool SQLiteDB::GetColumnName( int index, string& str )
	{
		if(m_sqlite_stmt != NULL)
		{
			const char* ptr = sqlite3_column_name(m_sqlite_stmt, index);
			if(ptr != NULL)
			{
				str = ptr;
				return true;
			}
		}
		return false;
	}

	bool SQLiteDB::GetColumnDeclareType( int index, string& str )
	{
		if(m_sqlite_stmt != NULL)
		{
			const char* ptr = sqlite3_column_decltype(m_sqlite_stmt, index);
			if(ptr != NULL)
			{
				str = ptr;
				return true;
			}
		}
		return false;
	}

	bool SQLiteDB::GetColumnType( int index, SQLiteDataType& data_type )
	{
		if(m_sqlite_stmt != NULL)
		{
			int sqlite3_type = sqlite3_column_type(m_sqlite_stmt, index);
			switch(sqlite3_type)
			{
			case SQLITE_INTEGER:
				data_type = ESD_INTEGER;
				break;

			case SQLITE_FLOAT:
				data_type = ESD_FLOAT;
				break;

			case SQLITE3_TEXT:
				data_type = ESD_TEXT;
				break;

			case SQLITE_BLOB:
				data_type = ESD_BLOB;
				break;

			case SQLITE_NULL:
				data_type = ESD_NULL;
				break;

			default:
				return false;
			}

			// Get the SQLite data type success
			return true;
		}

		return false;
	}

	int SQLiteDB::GetColumnSize( int index )
	{
		if(m_result_code == SQLITE_ROW || m_sqlite_stmt != NULL)
			return sqlite3_column_bytes(m_sqlite_stmt, index);
		return 0;
	}

#if 0
	bool SQLiteDB::SetValue( int index, const void* value, int size )
	{
		if(m_reCode == SQLITE_ROW || m_sqlite_stmt != NULL)
		{
			sqlite3_value* tmp;
			int code = sqlite3_bind_value(m_sqlite_stmt, index);
		}

	}
#endif
}